﻿#ifndef COMMONINC_H
#define COMMONINC_H

#include "EagleDefine.h"
#include <functional>
#include "qobject.h"
#include "qthread.h"
#include "qmetatype.h"
#include "EagleApi.h"
#include <QDebug>
#include "qcoreevent.h"
#include "PluginMgr.h"
#include "TimePluginMgr.h"
#include "OverlapPluginMgr.h"
#include <atomic>
#include "QImage"
#include <fstream>
#include "QVariant"
#include "EagleLog.h"
#include "ConfigFile.h"
#include "QMessageBox"
#include <QCoreApplication>  // qApp


// FOR ffmpeg
#ifdef _WIN32
	#define snprintf _snprintf
	#pragma execution_character_set("utf-8")
#else#include <inttypes.h>#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0]))
#endif // 

#define WITH_FFMPEG
//#undef  WITH_FFMPEG

#ifdef WITH_FFMPEG
#ifdef __cplusplus
extern "C" {
#endif
#include <libavutil/avassert.h>
#include <libavutil/channel_layout.h>
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include "libavutil/mem.h"
#include "libavutil/fifo.h"
#include "libavcodec/avcodec.h"
#ifdef __cplusplus
}
#endif

#endif

class EagleMainFrame;
extern EagleMainFrame* g_theApp;

//#define _NODEVICE
// 自定义事件 [10/24/2016 ejitec]
extern QEvent::Type MyEventType;
extern QEvent::Type UiEventType;


#define CONFIG_TO_EXPORT 0
#define CONFIG_TO_REPLAY 1

#define TINY_THREAD_BEGIN std::async(std::launch::async,[=](){  
#define TINY_THREAD_END  });

typedef enum
{
	E_JOBTYPE_FRESHUI = 0,
	E_JOBTYPE_NORMAL,
	E_JOBTYPE_SCANNETWORK,
	E_JOBTYPE_SCANNDEVLIST,
	E_JOBTYPE_FETCHRECORDLIST,


	E_JOBTYPE_STREAM_REQ_BASE = 100,
	E_JOBTYPE_STREAM_REC_START,
	E_JOBTYPE_STREAM_REC_STOP,
	E_JOBTYPE_STREAM_REAL_START,
	E_JOBTYPE_STREAM_REAL_STOP,
	E_JOBTYPE_STREAM_EXP_START,
	E_JOBTYPE_STREAM_SIMU_START,
	E_JOBTYPE_STREAM_SIMU_STOP,
	E_JOBTYPE_STREAM_EXP_END,

	//////////////////////////////////////////////////////////////////////////
	// 与视频请求相关 [10/25/2016 ejitec]

}E_WorkType;

struct tagCbWork;

typedef std::function<bool ()> signalFun;
typedef std::function<void(const struct tagCbWork&)> doneFun;  // 定义槽函数

typedef struct tagCbWork
{
	bool m_bRetOK;
	E_WorkType m_eType;
	void* m_pContext;
	signalFun m_fWork;

}TCbJob;

Q_DECLARE_METATYPE(TCbJob)
Q_DECLARE_METATYPE(eagle_netcard_info)
Q_DECLARE_METATYPE(EagleData_Record_Id*)

/************************************************************************/
/* 
	与CCD关联 结构 
*/
/************************************************************************/
typedef struct tagCCDInfo
{
	QString m_strCCDNam;
	eagle_capture_config m_tInfo;
	bool m_bValid;
	tagCCDInfo()
	{
		m_bValid = true;
		memset(&m_tInfo, 0, sizeof(m_tInfo));
		m_strCCDNam = QObject::tr("NA");
		m_tInfo = { 0 };
	}

}TCCDInfo;

/************************************************************************/
/*
与 Device 关联 结构  一个设备包含多个 CCD
*/
/************************************************************************/
typedef struct tagDevWithCCDInfo
{
	float m_fTemp;
	int m_nDevID;
	QString m_strDevMode;
	QString m_strDevNam;
	EDeviceType m_device_type;
	eagle_system_status m_tStatus;
	eagle_camera_disk_config m_diskMap;
	QVector<TCCDInfo> m_vCCDInfo;

	tagDevWithCCDInfo()
	{
		m_strDevNam = QObject::tr("NA");
		m_strDevMode = QObject::tr("NA");
		m_fTemp = 30;
		m_device_type = EDeviceTypeCL;
		m_vCCDInfo.clear();
	}

}TDevWithCCDInfo;


// 状态栏 
enum{
	STATUS_BAR_INDEX_NOPE = 0,
	STATUS_BAR_INDEX_STATUS,//系统状态，就绪，采集，记录...,包括设备连接状态，如果未连接显示“未连接”，已连接显示设备状态
	STATUS_BAR_INDEX_INFOR,//FPGA温度
	STATUS_BAR_INDEX_CONMUNICATE_CHANNEL,
	STATUS_BAR_INDEX_MAX
};


class Worker
	:public QObject
{
	Q_OBJECT

	public slots :

	void on_DoThreadJob(TCbJob tCb)  // 在线程内执行
	{
		//qDebug() << "on_DoThreadJob() begin " << QThread::currentThread();

		if (tCb.m_fWork)
		{
			tCb.m_bRetOK = tCb.m_fWork();
		}
		//qDebug() << "on_DoThreadJob() end " << QThread::currentThread(); 

		emit evt_Finished(tCb);  // 传出操作结果
	}

signals:

	void evt_Finished(TCbJob tCb);

};


class EvtThreadHandler
	:public QObject
{
	Q_OBJECT

	QThread m_thread;

public:

	EvtThreadHandler(QObject* ptCaller, doneFun pf)
	{
		m_ptCaller = ptCaller;

		m_cWorker.moveToThread(&m_thread);
		connect(this, &EvtThreadHandler::evt_toDoCommonWork, &m_cWorker, &Worker::on_DoThreadJob);
		// 避免外部 连接
		connect(&m_cWorker, &Worker::evt_Finished, ptCaller, pf);
		m_thread.start(QThread::NormalPriority);
	}

	~EvtThreadHandler()
	{
		m_thread.quit();
		m_thread.wait(1*1000);
		qDebug() << "EvtThreadHandler thread  quitted ..." << QThread::currentThread(); 
	}

	void postJob(signalFun cb, TCbJob tContext = TCbJob())
	{
		tContext.m_fWork = cb;
		emit evt_toDoCommonWork(tContext);   // 在线程中响应 信号 并处理绑定的 函数任务
	}

signals:

	void evt_toDoCommonWork(TCbJob tCb);

private:
	QObject*  m_ptCaller;
	Worker m_cWorker;
};


typedef enum{
	DISPLAY_CONTEXT_BASE = 0,
	DISPLAY_CONTEXT_REALTIME,
	DISPLAY_CONTEXT_REPLAY,
	DISPLAY_CONTEXT_EXPORT,
	DISPLAY_CONTEXT_MAX
}DISPLAY_CONTEXT_TYPE;


//typedef enum{
//	DISPLAY_TYPE_NONE = 0,
//	DISPLAY_TYPE_REALTIME,
//	DISPLAY_TYPE_REPLAY,
//	DISPLAY_TYPE_MAX
//}CURRENT_DISPLAY_TYPE;

typedef enum{
	DISPLAY_MODE_NORMAL = 0,
	DISPLAY_MODE_SMALL
}DISPLAY_MODE_TYPE;



/************************************************************************/
/*	
	Eagle自定义消息类型   对应不同的操作类型
*/
/************************************************************************/
typedef enum
{
	GT_CMD_NONE = 0,
	GT_CMD_REALTIME_START_PLAY,
	GT_CMD_REALTIME_STOP_PLAY,
	GT_CMD_REPLAY_START,
	GT_CMD_REPLAY_STOP,
	GT_CMD_REPLAY_PAUSE,
	GT_CMD_REPLAY_RESUME,
	GT_CMD_REPLAY_NEXT,
	GT_CMD_REPLAY_PREV,
	GT_CMD_REPLAY_FASTA,
	GT_CMD_REPLAY_FASTB,
	GT_CMD_EXPORT_START,
	GT_CMD_EXPORT_CANCLE,  //  新增一个消息 处理取消任务
	GT_CMD_EXPORT_STOP,  // 用于内存回收 在所有导出线程停止工作时 作用
	GT_CMD_CHECK_DATA,
	GT_CMD_CHECK_STOP,
	GT_CMD_SEND_CCD_DETECT_INFO,
	GT_CMD_SWITCH_DISPLAY_MODE,  // no use
	GT_CMD_EXIT,  // no use
	GT_CMD_MAX
}GT_CMD;



typedef std::function<void()> UIEventCallbackType;

class PostToUIEvent :public QEvent
{
public:
	PostToUIEvent(UIEventCallbackType callback)
		:QEvent(UiEventType), cb(callback)
	{

	}
	PostToUIEvent(const PostToUIEvent& event)
		:QEvent(event.type())
	{
		cb = event.cb;
	}
	void Callback()
	{
		cb();
	}
	virtual QEvent *Clone() const
	{
		return new PostToUIEvent(*this);
	}

private:
	UIEventCallbackType cb;
};

class EaglePostEventManager 
	:public QObject
	
{
public:

	explicit EaglePostEventManager(QObject *parent)
		:QObject(parent)
	{

	}

	~EaglePostEventManager()
	{
	}

	void Post(PostToUIEvent* event)
	{
		qApp->postEvent(this,event);
	}
	
	void Send(PostToUIEvent* event)
	{
		qApp->sendEvent(this, event);
	}

	void Post(UIEventCallbackType callback)
	{
		PostToUIEvent* e = new PostToUIEvent(callback);
		
		Post(e);
	}
	
	void Send(UIEventCallbackType callback)
	{
		PostToUIEvent* e = new PostToUIEvent(callback);
		
		Send(e);
	}


protected:

	void customEvent(QEvent *event)
	{
		PostToUIEvent* e = static_cast<PostToUIEvent*>(event);
		if (e)
		{
			e->Callback();
		}
	}
};

struct MessageCmd
	:public QEvent
{
	MessageCmd()
		:QEvent(MyEventType)
		, cmd(GT_CMD_NONE)
	{
// 		qDebug() << "MessageCmd() cst called," 
// 		<< "MyEventType:" << static_cast<ushort>(MyEventType);
	}

	MessageCmd(int nMsgID)    // forDebug [11/7/2016 ejitec]
		:QEvent(MyEventType), cmd(nMsgID)
	{
		cmd = nMsgID;
// 		qDebug() << "MessageCmd() cst called,"
// 			<< "MyEventType:" << static_cast<ushort>(MyEventType) << ",CMD:" << cmd;
	}

	MessageCmd(const MessageCmd&  other)
		:QEvent(other.type())
	{
		*this = other;
// 		qDebug() << "MessageCmd() Cpycst called,"
// 			<< "MyEventType:" << static_cast<ushort>(MyEventType) << ",CMD:" << cmd;
	}

	~MessageCmd()
	{
		//qDebug() << "MessageCmd() dst called ..." << ",CMD:" << cmd;
	}

	int cmd;
	//common items needed by realtime and replay
	wchar_t task_name[MAX_NAME_LEN];
	unsigned int task_index;
	unsigned int record_index;
	unsigned int device_id;//add for cluster
	unsigned int ccd_serial;
	time_t	record_time;
	unsigned int line;
	unsigned int column;
	unsigned int color_depth;
	unsigned int frame_rate;
	unsigned char lose_frame;		//是否丢帧：GT_CMD_SEND_CCD_DETECT_INFO使用
	unsigned int sampling_format;	// add for sdi decoder.
	long interval;//抽帧间隔
	int		replay_export_method;	//复用在回放和导出中作为0、表示按帧数，1、按时间回放
	unsigned long frame_time_interval;	//时间回放时，时间对齐数

	union
	{
		struct{
			//items needed by replay
			long replay_start_index;
			long replay_end_index;
		}Replay;
		struct {
			//items needed by export
			long export_start_index;
			long export_end_index;
			long export_type;//bmp/tiff...
			long export_decode_method;//not use
			wchar_t export_path[MAX_NAME_LEN];
		}Export;
		struct {
			unsigned int start_frame;	//start from 0;
			unsigned int read_frame_num;
			unsigned short frame_rate;
			unsigned short frame_taps;
		}Simu;
	};
};


class QDisPlayWidget;


class BaseDisplayContext
{
public:
	BaseDisplayContext(MessageCmd &cmd,
		EagleData_CcdRecord *ccdrec, QDisPlayWidget* pWnd)
		:_pix_size((cmd.color_depth + 7) / 8)
		, valid(true)
		, _ccdrec(ccdrec)
		, m_PlayWnd(pWnd)
	{
		_task_name = wstring(cmd.task_name);
		_ccd_serial = cmd.ccd_serial;
		_device_id = cmd.device_id;
		_line = cmd.line;
		_column = cmd.column;
		_color_depth = cmd.color_depth;
		context_type = DISPLAY_CONTEXT_BASE;
        wchar_t ccd_name[32] = { '\0' };
        EagleControl_GetCameraName(_device_id, _ccd_serial, ccd_name);
        _ccdNam = ccd_name;
	}

	virtual ~BaseDisplayContext(){}

	virtual DISPLAY_CONTEXT_TYPE get_type() { return context_type; }

public:
    wstring get_ccdNam(){ return _ccdNam; }
	int get_device_id() { return _device_id; }
	int get_ccd_serial() { return _ccd_serial; }
	//	wstring &get_ccd_name() { return _ccd_name; }
	int get_color_depth() { return _color_depth; }
	int get_line() { return _line; }
	int get_column() { return _column; }
	int get_pix_size() { return _pix_size; }
	wstring get_task_name() { return _task_name; }
	EagleData_CcdRecord *get_related_ccd_rec(){ return _ccdrec; }
    

protected:

	void* handle;
	wstring _task_name;
	int	_device_id;
	int _ccd_serial;
	int _line;
	int _column;
	int _color_depth;
	int _pix_size;
    wstring _ccdNam;
	EagleData_CcdRecord* _ccdrec;
public:
	DISPLAY_CONTEXT_TYPE context_type;
	std::atomic<bool> valid;
	QDisPlayWidget* m_PlayWnd;
};

//////////////////////////////////////////////////////////////////////////
// 实时流的 ccdrec->id 由cmd 生成，然后传入 baseCtx
class RealtimeContext :public BaseDisplayContext
{
public:
	RealtimeContext(MessageCmd &cmd,
		EagleData_CcdRecord *ccdrec, QDisPlayWidget* pWnd)
		:BaseDisplayContext(cmd, ccdrec, pWnd)
	{
		context_type = DISPLAY_CONTEXT_REALTIME;
		ccdrec->id.device_id = cmd.device_id;
		current_frame_index = 0;
		ccdrec->id.ccd_serial = cmd.ccd_serial;
	}
	~RealtimeContext(){}

public:

	long current_frame_index;
	unsigned long last_gps_time;
};

class ReplayContext :public BaseDisplayContext
{
public:
	ReplayContext(MessageCmd &cmd, EagleData_CcdRecord *ccdrec, QDisPlayWidget* pWnd)
		:BaseDisplayContext(cmd, ccdrec, pWnd)
	{
		replay_mothod = cmd.replay_export_method;
		frame_time_interval = cmd.frame_time_interval;
		replay_start_index = cmd.Replay.replay_start_index;
		replay_end_index = cmd.Replay.replay_end_index;
		replay_select_interval = cmd.interval;
		current_frame_index = replay_start_index;
		fast_replay = 1;
		context_type = DISPLAY_CONTEXT_REPLAY;
		m_bSinglePlay = false;

		GenerateTimeToIndexMap();
	}
	~ReplayContext(){ time_to_index.clear(); }

public:
	void GenerateTimeToIndexMap();
	//private:
public:
	//replay needed extra info
	int	 replay_mothod;
	unsigned long frame_time_interval;
	long replay_start_index;
	long replay_end_index;
	long replay_select_interval;

	//replay status in process
	long current_frame_index;
	std::mutex lock;//lock to change current_frame_index and current_frame_time
	int fast_replay;
	std::atomic<bool> m_bSinglePlay;
	std::map<long, long> time_to_index;
};


extern QStringList  g_strListDlgTiltle;

// 导出进度对话框 [11/9/2016 ejitec]
enum
{
	EAGLE_PROGRESS_DLG_TITLE_EXPORT = 0,
	EAGLE_PROGRESS_DLG_TITLE_CHECK,
	EAGLE_PROGRESS_DLG_TITLE_EXCEPTION_SCAN,
	EAGLE_PROGRESS_DLG_TITLE_MAX
};

#ifdef WITH_FFMPEG

typedef struct OutputStream {
	AVStream *st;
	AVCodecContext *enc;
	AVFrame *frame;
	AVFrame *tmp_frame;
	struct SwsContext *sws_ctx;
} OutputStream;

#endif

#ifdef _WIN32
#define ASYNC_FLIEWRITE
#endif

// 导出上下文 [11/8/2016 ejitec]
class ExportContext :public BaseDisplayContext
{
public:
	ExportContext(MessageCmd &cmd,
		EagleData_CcdRecord *ccdrec, QDisPlayWidget* pWnd)
		:BaseDisplayContext(cmd, ccdrec,pWnd),
		export_canceled(false), destroyed(false),m_dwErrPixs(0)
	{
		//		start_frame_index = cmd.start_frame_index;
		//		end_frame_index = cmd.end_frame_index;
		export_mothod = cmd.replay_export_method;
		frame_time_interval = cmd.frame_time_interval;
		export_start_index = cmd.Export.export_start_index;
		export_end_index = int(cmd.Export.export_end_index);
		export_select_interval = cmd.interval;  //
		export_type = cmd.Export.export_type;
		export_decode_method = cmd.Export.export_decode_method;
		export_path = QString::fromWCharArray(cmd.Export.export_path);
		current_frame_index = cmd.Export.export_start_index;
		liveThreadCnt = 1;
		m_vExpIndexs.clear();
		GenerateTimeToIndexMap();

		context_type = DISPLAY_CONTEXT_EXPORT;

		qDebug() << "CCD_" << get_device_id() << "_" << get_ccd_serial()<< " time_to_index size:" << time_to_index.size()
			<< " endIdx:" << export_end_index;

#ifdef WITH_FFMPEG
		output_format_context = NULL;
		memset(&video_output_stream, 0x00, sizeof(video_output_stream));
#endif 
	}

	~ExportContext()
	{
	}
public:
	void GenerateTimeToIndexMap();
	int check();
	int check(int n);
	bool export_finished() { return current_frame_index.load() > export_end_index; }  // ">=" modifyto ">" for the lastOne frame
	long get_current_export_percent() { long cur_index = current_frame_index.load(); return (cur_index - export_start_index) * 100 / (export_end_index - export_start_index); }
	long get_current_export_num() { return current_frame_index.load(); }
	void set_current_export_num(long index) { current_frame_index.store(index); }
	void cancel_export() { export_canceled = true; }
	bool is_export_canceled() { return export_canceled; }
	QString &get_export_path() { return export_path; }
	long get_export_frame_num() { return (export_end_index - export_start_index) / export_select_interval; }

public:

	uint32_t export_mothod;
	uint32_t frame_time_interval;
	int32_t export_start_index;
	int32_t export_end_index;
	uint32_t export_select_interval;
	uint32_t export_type;//bmp/tiff...
	uint32_t export_decode_method;
	QString export_path;
	std::map<int32_t, int32_t> time_to_index;
	QVector<long> m_vExpIndexs;  // 用于分布式 运算
	
 	//export realtime information
	atomic<long> current_frame_index;
	atomic<int> total_export_num;
	atomic<long> liveThreadCnt;
	atomic<quint64> m_dwErrPixs;
	bool export_canceled;

public:

	bool destroyed;
public:

	std::vector<long> export_frame_index;
	std::mutex export_frame_index_lock;
	std::mutex export_log_write_lock;

public://export avi video
	//	cv::VideoWriter *videowriter;
public://export raw stream1
	std::ofstream raw_stream1_stream;

#ifdef ASYNC_FLIEWRITE
	HANDLE raw_stream1_fileHd;
#endif

private://for check
	bool show_first_err_frame;

public: //Add for export AVI
	//	int export_avi_level;
	//	unsigned long long file_size_limit;
	//	unsigned long long current_export_size;
	//	int avi_index;
	int _width;
	int _height;

public:
	// Add for test export avi using ffmpeg

#ifdef WITH_FFMPEG
	AVFormatContext *output_format_context;  //format_context
	OutputStream	video_output_stream;  //outputstream
#endif //XP_VERSION

};


// 仿真上下文 [11/28/2017 xystar]
class SimuReplayContext :public BaseDisplayContext
{
public:
	SimuReplayContext(MessageCmd &cmd,
		EagleData_CcdRecord *ccdrec, QDisPlayWidget* pWnd)
		 :BaseDisplayContext(cmd, ccdrec, pWnd)
	{
		replay_start_index = cmd.Simu.start_frame;
		replay_end_index = cmd.Simu.start_frame + cmd.Simu.read_frame_num - 1;
		frame_rate = cmd.Simu.frame_rate;
		frame_taps = cmd.Simu.frame_taps;
		current_frame_index = replay_start_index;
		context_type = DISPLAY_CONTEXT_REPLAY;
		current_start_index = 0;
		m_bSinglePlay = false;
		m_bSeek = false;
		generate_time_map();
	}
	~SimuReplayContext(){  }

private:
	void generate_time_map();
public:
	//replay needed extra info
	long replay_start_index;
	long replay_end_index;

	unsigned short frame_rate;
	unsigned short frame_taps;


	//replay status in process
	long current_frame_index;
	long current_start_index;
	unsigned long start_frame_time;
	unsigned long end_frame_time;
	unsigned long start_index_head_index;
	std::mutex lock;//lock to change current_frame_index and current_frame_time
	atomic<long> m_nWorkThreadCnt;
	atomic<bool> m_bSinglePlay;
	atomic<bool> m_bSeek;
	atomic<bool> m_bSingleMode;  // true -- next  false -- prev
};



// 记录回放 时的 窗口上下文
struct ExportReplayContextCcd
{
	//In
	int device_id;
	int ccd_index;//set to 1

	//Out
	bool checked;
	long start_frame_index;
	long end_frame_index;
	long interval;//抽帧间隔，默认为1，不抽帧
	long time_interval; //时间对齐, 默认为0, 不对齐
};


struct ConfigExportReplayContext
{
	//In
	std::wstring task_name;//name of the task
	time_t record_time;
	int export_or_replay;//0:export, 1:replay
	int index_or_time; //0:index, 1:time

	//Out for export
	QString export_path;
	long export_type;

	std::vector<ExportReplayContextCcd> ccd_context;
public:
	ConfigExportReplayContext() :task_name(L""), record_time(0), export_path(""), export_type(0), index_or_time(0){
		ccd_context.clear();
	}
};


typedef enum
{
	FRAME_TYPE_DEFAULT,//default frame of StaticFrameMgr,black frame
	FRAME_TYPE_NORMAL,//normal replay frame
	FRAME_TYPE_STATIC,//static frame opened from file or other place
	FRAME_TYPE_MAX
}FRAME_TYPE;

// Queen 数据结构 [11/11/2016 ejitec]
struct DisplayFrameData
{
	DisplayFrameData() :buf(NULL), rotated(false), displayed(false){}
	bool displayed;
	FRAME_TYPE frame_type;
	unsigned char *buf;//解码后8bit数据，包含RGB分量，空间大小为_column * _line * 3
	unsigned char *orig_data;//原始非压缩数据
	unsigned char *read_format_data;//orig_data通过readout format调整像素位置后的数据
	unsigned char *frame_head;//帧头数据
	bool rotated;//解码后的8bit数据buf是否有旋转，column和line为旋转前的分辨率不变
	int orig_data_len;//orig_data的buffer长度（也是read_format_data的buffer长度）
	int decoded_buf_len;//buf的buffer长度
	int frame_head_len;//frame_head的buffer长度

	//orig_data的图像参数
	int line;
	int column;
	int color_depth;
	int real_color_depth;//10bit时，color_depth = 12, real_color_depth = 10；14bit时，color_depth = 16，real_color_depth = 14

	//read_format_xxx是通过ReadoutFormat变换后的图像参数
	int read_format_column;
	int read_format_line;
	int read_format_color_depth;

	//decoded_xxx是通过Decode后的图像参数，通常情况下decode后column和line不变，color_depth = 8
	//decoder的输入参数是read_format的输出参数，即read_format_xxx
	//FIXME:目前只支持decoder后的参数和read_format_xxx参数相同（除了color_depth），否则在暂停框图等地方会有问题！
	//				暂停框图和显示像素值以read_format_xxx为准（需要显示原始值），但界面显示的图像参数是decoded_xxx！
	//				snapshot和statistics地方可能也会有问题，如果以后做图像框选缩放等动作，这个地方需要注意（20150424）
	int decoded_column;
	int decoded_line;
	int decoded_color_depth;

	//frame data info
	unsigned short h;
	unsigned short m;
	unsigned short s;
	unsigned short ms;
	unsigned short rate;
	long frame_index;
	unsigned int tag;
	void set_display(bool display) { displayed = display; }
};
#pragma endregion 上下文数据结构


//////////////////////////////////////////////////////////////////////////
// 默认界面静态数据,实际不再使用 [11/11/2016 ejitec]
#pragma region 静态数据

class StaticFrameMgr
{
private:
	StaticFrameMgr(QString strMidTxt = "NoVideo");

	~StaticFrameMgr()
	{
		delete[] DefaultStaticData.orig_data;
		delete[] DefaultStaticData.read_format_data;
		delete[] DefaultStaticData.buf;
		if (CurrentStaticData.orig_data && CurrentStaticData.buf && CurrentStaticData.read_format_data) {
			delete[] CurrentStaticData.buf;
			delete[] CurrentStaticData.orig_data;
			delete[] CurrentStaticData.read_format_data;
		}
	}

	StaticFrameMgr(const StaticFrameMgr&){}
	StaticFrameMgr& operator=(const StaticFrameMgr&){}

private:
	QString m_strNam;
public:
	static StaticFrameMgr* getInstance()
	{
		static StaticFrameMgr _instance;
		return &_instance;
	}

	DisplayFrameData &GetCurrentStaticData() { return CurrentStaticData; }
	DisplayFrameData &GetDefaultData();
	QImage& GetDefaultImage();

	//void SetCurrentStaticData(StatisticsSubRIO &rio);
private:
	DisplayFrameData CurrentStaticData;
	DisplayFrameData DefaultStaticData;//default frame
	QImage m_defaultImg;
};


//////////////////////////////////////////////////////////////////////////
// 导出相关 [11/7/2016 ejitec]
struct BatchExportContext
{
	//Out for export
	QString export_path;
	long export_type;

	std::vector<EagleData_Record_Id*> remain_export_record;
	std::vector<EagleData_Record_Id*> exported_records;

	//	bool has_one_record_exported;
	bool cancel_export;

	//	int		export_status;

	std::mutex	export_context_lock;
public:
	BatchExportContext() :export_path(), export_type(0), cancel_export(false)
	{
		remain_export_record.clear(); exported_records.clear();
	}
	~BatchExportContext(){
		remain_export_record.clear(); exported_records.clear();
	}
};
#pragma endregion 静态数据


//////////////////////////////////////////////////////////////////////////
// 几个列表框标题 [11/11/2016 ejitec]

#pragma region 浮窗列表框

struct CLMode{
	int tap;
	int bits;
};
//Base Mode:Camera 1,2,3,4
static CLMode CLModeBase[] = { { 1, 8 }, { 2, 8 }, { 3, 8 }, { 1, 10 }, { 2, 10 }, { 1, 12 }, { 2, 12 }, { 1, 14 }, { 1, 16 } };
//Medium Mode: Camera 1,2
static CLMode CLModeMedium[] = { { 4, 8 }, { 3, 10 }, { 4, 10 }, { 3, 12 }, { 4, 12 } };
//Full Mode: Camera 1
static CLMode CLModeFull[] = { { 8, 8 }, { 8, 12 }, { 8, 10 }, { 10, 8 } };

static CLMode CLModeNull[] = { { 'x', 'x' } };

#define ITEM_PARENT  (QTreeWidgetItem::UserType + 1)
#define ITEM_CHILD  (QTreeWidgetItem::UserType + 2)

typedef enum {
	BASE_CLIENT_DATA,
	DEVICE_CLIENT_DATA,
	CCD_CLIENT_DATA,
	CLIENT_DATA_MAX
} CLIENT_DATA_TYPE;

typedef enum{
	CameraModeTypeBase = 0,
	CameraModeTypeMedium,
	CameraModeTypeFull,
	CameraModeTypeNotGeneric,
	CameraModeTypeNull,
}CameraModeType;

typedef enum{
	SDICameraModeTypeYCbCr422_I = 1,
	SDICameraModeTypeYCbCr422_P,
	SDICameraModeTypeNotGeneric,
}SDICameraModeType;

static QStringList InterfaceType =  QStringList() << "CL" << "SDI";
static QStringList CaptureStatus 

	= QObject::tr("就绪|预览|记录|暂停").split("|", QString::SkipEmptyParts);
static QStringList StatBarStatus = 
	QObject::tr("设备未连接 | 就绪 | 预览 | 记录 | 暂停, FPGA温度, 剩余容量：磁盘").split(" | ", QString::SkipEmptyParts);


const static QStringList CL_Mode_Str = QString("Base|Medium|Full|NG|NA").split("|", QString::SkipEmptyParts);
const static QStringList CL_Mode_Chr = QString("B|M|F|NG|NA").split("|", QString::SkipEmptyParts);
const static QStringList SDI_Mode_Str = QString("|YCbCr4:2:2 I|YCbCr4:2:2 P|NG").split("|", QString::KeepEmptyParts);

typedef struct SDIResolution{
	unsigned long column;
	unsigned long line;
}SDIResolution;

static SDIResolution currentSupportResolution[] = { { 1920, 1081 }, { 1280, 720 } };


class tagItemDataBase
	:public QVariant
{

private:

};

class tagItemDataDev
	:public tagItemDataBase
{
public:
	TDevWithCCDInfo data;
};

class tagItemDataCCD
	:public tagItemDataBase
{

public:
	TDevWithCCDInfo data;
};

class tagItemDataRecord
	:public tagItemDataBase
{

public:

	EagleData_CcdRecord_Id data;
};


struct ListLabel{
	QString _label;
	int colwidth;
};//Label

enum {
	CCD_TREELIST_COLOMN_DEVICECCD = 0,
	CCD_TREELIST_COLOMN_TYPEMODE,
	CCD_TREELIST_COLOMN_TEMP,
	CCD_TREELIST_COLOMN_DISK0,
	CCD_TREELIST_COLOMN_DISK1,
	CCD_TREELIST_COLOMN_DISK2,
	CCD_TREELIST_COLOMN_DISK3,
	CCD_TREELIST_COLOMN_DISK4,
	CCD_TREELIST_COLOMN_DISK5,
	CCD_TREELIST_COLOMN_DISK6,
	CCD_TREELIST_COLOMN_DISK7,
	CCD_TREELIST_COLOMN_COLUMN,
	CCD_TREELIST_COLOMN_LINE,
	CCD_TREELIST_COLOMN_COLORDEPTH,
	CCD_TREELIST_COLOMN_TAPS,
	CCD_TREELIST_COLOMN_STATUS
};
//#define 

#define CCD_TREELIST_COLUMN_NUM 16
static const struct ListLabel ccd_treelist_label[CCD_TREELIST_COLUMN_NUM] =
{
	{ QObject::tr("设备/相机"), 120 },
	{ QObject::tr("设备/相机模式"), 100 },
	{ QObject::tr("设备温度"), 100 },
	{ QObject::tr("磁盘0"), 50 },
	{ QObject::tr("磁盘1"), 50 },
	{ QObject::tr("磁盘2"), 50 },
	{ QObject::tr("磁盘3"), 50 },
	{ QObject::tr("磁盘4"), 50 },
	{ QObject::tr("磁盘5"), 50 },
	{ QObject::tr("磁盘6"), 50 },
	{ QObject::tr("磁盘7"), 50 },
	{ QObject::tr("列数"), 40 },
	{ QObject::tr("行数"), 40 },
	{ QObject::tr("色深"), 40 },
	{ QObject::tr("Taps"), 40 },
	{ QObject::tr("状态"), 60 }
};

#define EVENT_LIST_COLUMN_NUM 3
static const struct ListLabel event_list_label[EVENT_LIST_COLUMN_NUM] =
{
	{ QObject::tr("类别"), 80 },
	{ QObject::tr("时间"), 100 },
	{ QObject::tr("详述"), 260 }
};

#define CL_CCD_LIST_COLUMN_NUM 8
static const struct ListLabel cl_ccd_list_label[CL_CCD_LIST_COLUMN_NUM] =
{
	{ QObject::tr("序号"), 30 },
	{ QObject::tr("模式"), 60 },
	{ QObject::tr("列数"), 40 },
	{ QObject::tr("行数"), 40 },
	{ QObject::tr("色深"), 40 },
	{ QObject::tr("Taps"), 40 },
	{ QObject::tr("状态"), 60 },
	{ QObject::tr("磁盘"), 40 }
};

#define SDI_CCD_LIST_COLUMN_NUM 6
static const struct ListLabel sdi_ccd_list_label[SDI_CCD_LIST_COLUMN_NUM] =
{
	{ QObject::tr("序号"), 30 },
	{ QObject::tr("模式"), 100 },
	{ QObject::tr("行数"), 40 },
	{ QObject::tr("色深"), 40 },
	{ QObject::tr("状态"), 40 },
	{ QObject::tr("磁盘"), 40 }
};

#define DEVICE_LIST_COLUMN 4
static const struct ListLabel device_list_label[DEVICE_LIST_COLUMN] =
{
	{ QObject::tr("设备ID"), 50 },
	{ QObject::tr("接口类型"), 60 },
	{ QObject::tr("FPGA 温度"), 80 },
	{ QObject::tr("磁盘剩余容量"), 180 }
};

#define TASK_LIST_COLUMN_NUM 4
static const struct ListLabel task_list_label[TASK_LIST_COLUMN_NUM] =
{
	{ QObject::tr("任务/记录"), 140 }
};

#define REC_LIST_COLUMN_NUM 9
static const struct ListLabel rec_list_label[REC_LIST_COLUMN_NUM] =
{
	//CcdRecord
	{ QObject::tr("设备"), 40 },
	{ QObject::tr("相机"), 40 },
	{ QObject::tr("接口类型"), 60 },
	{ QObject::tr("列数"), 50 },
	{ QObject::tr("行数"), 50 },
	{ QObject::tr("色深"), 40 },
	{ QObject::tr("帧数"), 60 },
	{ QObject::tr("开始时间"), 120 },
	{ QObject::tr("结束时间"), 120 }
};

#define STATUS_LIST_COLUMN_NUM 4
static const struct ListLabel status_list_label[STATUS_LIST_COLUMN_NUM] =
{
	{ QObject::tr("系统状态"), 100 },
	{ QObject::tr(" "), 150 },
	{ QObject::tr("磁盘剩余容量"), 100 },
	{ QObject::tr(" "), 150 }
};
#pragma endregion 浮窗列表框


// for xi guang suo   时间、AE、帧偏移 [6/1/2017 xystar]
typedef std::tuple<int, unsigned int, int>  XGSExpParams;

//////////////////////////////////////////////////////////////////////////
// function [11/4/2016 ejitec]

int EagleLibReadOneStoreFrameAndDecode(IN EagleData_Record_Id rec_id,
	IN EagleData_CcdRecord_Id ccd_rec_id,
	IN long frame_index,
	IN DisplayFrameData* frame_data,
	IN std::wstring read_format_name,
	IN bool need_decoder,
	IN std::wstring decoder_name,
	IN DecodeRequestStruct* decode_para);

bool SaveRawFile(char *data, int size, const QString& name);
bool SaveImageFile(char *data, int column, int line, int type, const QString &name);
bool SaveImageFile(QImage& img, int type, const QString& name);
void WriteExportLog(BaseDisplayContext* context, QString strMsg);
void CheckExportFrameIndex(ExportContext& context);
// CCD、dev 列表框 [11/15/2016 ejitec]
QStringList GenerateDeviceRow(const eagle_system_status* status, int device_id, EDeviceType device_type);
QStringList GenerateCcdRow(const eagle_capture_config* ccdconfig, unsigned char diskmap, int device_id, EDeviceType device_type);
void setGloabWndStyle();

//  [11/15/2016 ejitec]
static inline CameraModeType GetCameraMode(QString &mode)
{
	for (int i = 0; i < CL_Mode_Str.size(); i++) {
		if (CL_Mode_Str[i] == mode)
			return (CameraModeType)i;
	}
	return (CameraModeType)0;
}

static inline CameraModeType GetCameraMode(int tap, int bits)
{
	for (quint32 i = 0; i < ARRAYSIZE(CLModeBase); i++){
		if (CLModeBase[i].bits == bits && CLModeBase[i].tap == tap)
			return CameraModeTypeBase;
	}
	for (quint32 i = 0; i < ARRAYSIZE(CLModeMedium); i++){
		if (CLModeMedium[i].bits == bits && CLModeMedium[i].tap == tap)
			return CameraModeTypeMedium;
	}
	for (quint32 i = 0; i < ARRAYSIZE(CLModeFull); i++){
		if (CLModeFull[i].bits == bits && CLModeFull[i].tap == tap)
			return CameraModeTypeFull;
	}
	for (quint32 i = 0; i < ARRAYSIZE(CLModeNull); i++) {
		if (CLModeNull[i].bits == bits && CLModeNull[i].tap == tap)
			return CameraModeTypeNull;
	}
	return CameraModeTypeNotGeneric;
}

static inline QString GetCaptureStatus(int nstat)
{
	if (nstat >= CaptureStatus.size())
	{
		return QString("");
	}

	return CaptureStatus[nstat];
}


//////////////////////////////////////////////////////////////////////////
// 提示 信息 [12/2/2016 ejitec]
#include <QIcon>
inline void wxMessageBox(QString text, QString title = "Message",QWidget* wgt = nullptr)
{
	QMessageBox box(wgt);
	box.setWindowTitle(title);
	box.setText(text);
	box.setIcon(QMessageBox::Warning);
	if (wgt)
		box.setWindowIcon(wgt->windowIcon());
	box.exec();

	//QMessageBox::warning(wgt, title, text);
}

inline const QString GetMessageBoxTitle()
{
	return QObject::tr("Eagle图像采集设备控制软件");
}

QString GetLogFormat(EagleLogIndexType index);
void ControlApiLogger(int ret, EagleLogIndexType index,QString str1 = "",QString str2 = "");
void ControlApiLogger(int ret, EagleLogIndexType index, int);
void ControlApiLogger(QString str);

#endif